﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using System.IO;
using System.Security;
using EasyStep.Attributes;


namespace EasyStep.Common
{
    public static class TypeFinder
    {
        private static Assembly[] _allAssemblies = null;
        public static string AssembliesFolderPath { get; set; }
        public static void Initialize(string assembliesFolderPath)
        {
            _allAssemblies = null;
            AssembliesFolderPath = assembliesFolderPath;
            if (!string.IsNullOrEmpty(assembliesFolderPath) && !System.IO.Directory.Exists(assembliesFolderPath))
                System.IO.Directory.CreateDirectory(assembliesFolderPath);
        }
        private static  object lockobj = new object(); 
        public static IEnumerable<Assembly> GetAllAssemblies()
        {
            if (_allAssemblies == null)

                lock (lockobj)
                {
                    if (!string.IsNullOrEmpty(AssembliesFolderPath))
                    {

                        if (_allAssemblies != null) return _allAssemblies;
                        try
                        {
                            var codeBase = Assembly.GetExecutingAssembly().CodeBase;
                            var uri = new Uri(codeBase);
                            var path = uri.LocalPath;
                            var binFolder = new DirectoryInfo(Path.GetDirectoryName(path));
                            var dllFiles = Directory.GetFiles(AssembliesFolderPath, "*.dll", SearchOption.TopDirectoryOnly).ToList();
                            _allAssemblies = dllFiles.Select(LoadAssembly).OrderBy(a =>
                            {
                                var n = a.GetName().Name.ToLowerInvariant();
                                n = n.Contains("action") ? "zzz" + n : n;//first load all assemblies, which help to work with actions=>selenium, webaii
                                return n;
                            }).ToArray();
                        }
                        finally
                        {

                        }
                    };
                    if (_allAssemblies == null)
                        _allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
                    else
                        _allAssemblies =_allAssemblies.Union(AppDomain.CurrentDomain.GetAssemblies()).ToArray();
                }
            return _allAssemblies;
        }
        public static Assembly LoadAssembly(string file)
        {
            Assembly result = null;
            try
            {
                result = Assembly.LoadFrom(file);
            }
            catch (Exception)
            {
                //Logger.Log.Error(string.Format("Can't load assembly:{0}, can't find actions in it!", file), ex);
            }
            return result;
        }
        private static readonly string[] KnownAssemblyExclusionFilter = new[]
        {
                    "System."                    
        };
        public static IEnumerable<Type> GetClassesWithAttribute<T>(IEnumerable<Assembly> assemblies)
        {
            assemblies = assemblies == null ? GetAllAssemblies() : assemblies;
            var hashCode = typeof(T).FullName.GetHashCode();
            return assemblies
                    .SelectMany<Assembly, Type>(a => a.GetTypes())
                    .Where(t => t.GetCustomAttributes(true).Where(o=>
                        o is T
                    ).Any());
        }
        public static List<Type> GetDerivedFrom<T>()
        {
            Type curType = typeof(T);
            return  GetAllAssemblies()
                    .SelectMany<Assembly, Type>(a => a.GetTypes())
                    .Where(t => t.GetInterfaces().Contains(curType))
                    .Where (t=>!t.IsAbstract && t.IsClass)
                    .ToList();
        }
        private static List<TestAction> _AllActionTypes;
        public static List<TestAction> AllActionTypes
        {
            get
            {
                if (_AllActionTypes == null || _AllActionTypes.Count==0)
                {
                    LoadActionTypes();
                }
                return _AllActionTypes;
            }
        }
        private static void LoadActionTypes()
        {
            var actionType = typeof(TestAction);
            var tempResult = new List<TestAction>();
            foreach (var t in GetClassesWithAttribute<TestAction>(null))
            {
                var ta = t.GetCustomAttributes(actionType, false) as TestAction[];
                if (ta == null || ta.Length != 1) continue;
                var taction = ta[0].Init(t);
                var existing = tempResult.Where(li => li.Id.Equals(taction.Id)).FirstOrDefault();
                if (existing !=null)
                {
                    throw new NotSupportedException(string.Format("Can't register action:{0} -{1},There is other action with same Id! {2} - {3} ", taction.Name, t.FullName, existing.Id, existing.CurrentType));
                };
                tempResult.Add(taction);
            }
            foreach (var a in tempResult)
            {
                a.Parent = tempResult
                    .Where(li => li.Id.Equals(a.ParentId))                    
                    .FirstOrDefault();
            }
            _AllActionTypes = tempResult;
        }
        public static TestAction GetAction(Guid actionId)
        {
            if (actionId == null) throw new InvalidDataException(string.Format("Could not load action type with id {0}, action is null"));
            if (actionId.Equals(Guid.Empty)) return null;

            var actionItem = AllActionTypes
                .Where(li => li.Id.Equals(actionId))                
                .FirstOrDefault();
            //if (actionItem == null) throw new InvalidDataException(string.Format("there no one such action with Id :{0}", actionId));
            return actionItem;
        }

        public static IEnumerable<TestAction> GetAllAction()
        {
            return AllActionTypes.OrderBy(i => i.OrderNumber).ThenBy(i=>i.Name);
        }
        public static IEnumerable<TestAction> GetChildrenActions(Guid parentId)
        {
            return AllActionTypes.Where(li => li.ParentId.Equals(parentId)).OrderBy(i => i.OrderNumber).ThenBy(i => i.Name);
        }
    }
}
